package nl.haemers.utils 
{
	import flash.utils.Dictionary;
	import imp.haemers.data.vo.CoreVO;
	import nl.haemers.data.constants.DataTypes;
	import nl.haemers.data.reflection.IProperty;
	import nl.haemers.data.reflection.Property;
	import nl.haemers.data.reflection.Type;
	import nl.haemers.data.serializers.ObjectSerializer;
	import nl.haemers.data.utils.ObjectUtils;
	/**
	 * ...
	 * @author Jonathan
	 * The ObjectReader analyses an Object and returns all it's properties incl. the values as a String.
	 * 
	 * Extra features:
		 * Set a limit on the depth
		 * Return a specific set of properties
	 */
	public class ObjectReader 
	{	
		private const NEW_LINE:String = "\n";
		private const TAB:String = "\t";
		
		private var _tabCounter:int = 0;
		
		/** Public methods **/
		public function ObjectReader() { }
		
		public function readObject(object:Object) : String
		{
			var type:Type = ObjectSerializer.getType(object);
			var result:String = appendLine(type.className);
			
			//We can't iterate over non-dynamic properties :( so we need to get our stuff out of the describeType method.
			result += createString(object, type);
			
			return result;
		}
		/** Getters & Setters **/
		
		/** Protected methods **/
		
		/** Private methods **/
		private function createString(object:Object, type:Type) : String
		{
			if (object == null)
				return "";
				
			var result:String = "";			
			var properties:Dictionary = type.getProperties();
			var hasProperties:Boolean;
			
			_tabCounter++;
			for (var property:String in properties)
			{
				hasProperties = true;
				var prop:IProperty = properties[property];
				if (ObjectUtils.isDataType(prop.dataType))
				{
					//parse normal data
					result += parse(object, prop.objectType, prop);	
					
				}
				else if (ObjectUtils.isCollectionType(prop.dataType))
				{
					//parse collection data
					result += parseCollectionToString(object[prop.propertyName], prop);
				}
				else if (ObjectUtils.isDictionary(prop.dataType))
				{
					result += parseDictionaryToString(object[prop.propertyName], prop);
				}
				else
				{
					//parse custom Object
					result += appendLine(prop.objectType.className);
					result += createString(object[prop.propertyName], prop.objectType);
				}
			}
			
			if (!hasProperties && type.isDynamic)
			{
				/*
				 * it's a dynamic object without predefined properties so we could loop through his properties which were
				 * added runtime!
				 */ 
				 result += parseDynamic(object);
			}
			_tabCounter--;
			
			return result;
		}
		
		private function parse(object:Object, type:Type, prop:IProperty) : String 
		{
			var result:String = "";
			if (type && type.isDynamic)
			{
				result += parseDynamic(object);
			}
			
			result += appendLine(prop.propertyName + ":" + prop.dataType + " = " +object[prop]);
			
			return result;
		}
		
		private function parseDynamic(object:Object) : String
		{
			var parsed:Boolean;
			var result:String = appendLine(String(object));
			
			_tabCounter++;
			for (var key:String in object)
			{
				parsed = true;
				result += appendLine(key + " = " + object[key]);
			}
			_tabCounter--;

			return result;
		}
		
		private function parseCollectionToString(object:Object, prop:IProperty) : String
		{
			if (object == null)
				return appendLine(prop.propertyName + ":" + prop.dataType + " = null");
			
			var result:String = "";
			var len:Number = object.length;			
			var type:Type = prop.objectType.collectionItemType;
			var prefix:String = "";
			var isVector:Boolean = ObjectUtils.isVector(prop.dataType);
			
			result += appendLine(prop.propertyName + ":" + prop.dataType + ": Length = " + len);
			if (isVector)
			{
				_tabCounter++;
				prefix += appendLine(prop.objectType.collectionItemType.className);
			}
			for (var j:int = 0; j < len; j++)
			{
				result += prefix + createString(object[j], prop.objectType.collectionItemType);
			}
			if (isVector)
			{
				_tabCounter--;
			}
			
			return result;
		}
		//Since a Dictionary is a sort of dynamic object we need to parse it differently.
		private function parseDictionaryToString(object:Object, prop:IProperty):String
		{
			if (object == null)
				return appendLine(prop.propertyName + ":" + prop.dataType + " = null");
			
			var result:String = "";
			var len:Number = 0;
			_tabCounter++;
			for (var key:String in object)
			{
				len++;
				result += appendLine(/*String(object) + ":" +*/ key + " = " + object[key]);
			}
			_tabCounter--;
			result = appendLine(prop.propertyName + ":" + prop.dataType + ": Length = " + len) + result;
			
			return result;
		}
		
		private function appendLine(str:String):String
		{
			var string:String = "";
			for (var i:int = 0; i < _tabCounter; i++)
			{
				string += TAB;
			}
			return string + str + NEW_LINE;
		}
	}

}